[FIX] databases: neutralize with the custom addons of the database - #180
Merged
Conversation
`odev quickstart` neutralized databases without ever running the neutralization scripts shipped by their custom modules, and ran `odoo-bin neutralize` with an `--addons-path` that did not contain the custom repository at all. Three separate defects had to line up for that: - `quickstart` linked the repository to the new database *after* restoring it, but neutralization runs from within `restore`. At that point the database had no repository, so no addons path could be derived from it. The repository is now linked before the restore, and again after it, since `restore` drops and recreates the database and clears its entry in the data store. - `additional_addons_paths` returned the repository root as-is. A repository keeping its modules in subdirectories is not itself a valid addons path, so it was filtered out downstream and never reached `--addons-path`. The lookup for the directories actually holding modules only existed in `OdoobinCommand`, which `odev neutralize` does not inherit from; it now lives on `OdoobinProcess` as `expand_addons_paths` and is shared by both. - The neutralization scripts of custom modules were looked up by intersecting the installed module names with the *directory names of the addons paths*. An addons path is a directory containing modules, never a module itself, so the intersection was always empty and the spinner reported "0 installed modules". The lookup now builds the module map from the subdirectories of each addons path. Also fixes an infinite loop in `LocalDatabase.neutralize`: the retry counter was reset by a walrus assignment evaluated on every iteration of the loop it was guarding, so `odev neutralize` on a database odev cannot start never returned. Closes #98
lse-odoo
previously approved these changes
Jul 31, 2026
|
|
||
| self._additional_addons_paths: list[Path] = [] | ||
| """List of additional addons paths to use when starting the Odoo process.""" | ||
| self._additional_addons_paths: list[Path] | None = None |
Contributor
There was a problem hiding this comment.
note: real sentinel values was introduced in python 3.15, see: https://peps.python.org/pep-0661/#rationale
Contributor
Author
There was a problem hiding this comment.
Good to know if we ever drop support for the precedent versions later
This was referenced Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
odev quickstartneutralized databases without ever running the neutralization scripts shipped bytheir custom modules, and ran
odoo-bin neutralizewith an--addons-paththat did not contain thecustom repository at all. Three independent defects had to line up for that:
quickstartlinked it after restoring the database, butneutralization runs from within
restore, when the database has no repository yet and no addonspath can be derived from it. It is now linked before the restore — and again after it, because
restoredrops and recreates the database, which deletes its row in the data store. Both callsare the same idempotent upsert.
additional_addons_pathsreturned it as-is, so arepository keeping its modules in subdirectories was filtered out downstream and never reached
--addons-path. The lookup for the directories actually holding modules existed only inOdoobinCommand, whichodev neutralizedoes not inherit from. It now lives onOdoobinProcessas
expand_addons_pathsand is shared by both, deduplicating candidates before filtering them sothe glob no longer runs once per module.
intersected with the directory names of the addons paths. An addons path is a directory
containing modules, never a module itself, so the intersection was always empty and the spinner
reported "0 installed modules". The module map is now built from the subdirectories of each addons
path, which also drops a nested loop that would have collected the same script once per addons
path.
Also fixes an infinite loop in
LocalDatabase.neutralize: the retry counter was reset by a walrusassignment evaluated on every iteration of the loop it was guarding, so
odev neutralizeon adatabase odev cannot start never returned. It has its own commit.
Note for reviewers
_additional_addons_pathsdefaults toNoneas a sentinel rather than[], so the expansion runsonce even when it legitimately finds nothing, and the property keeps handing back the same list
object —
odev-plugin-ai-translationappends to it.Making
NeutralizeCommandanOdoobinCommandwould also have fixed the addons path, and wasrejected: its
__init__chains into_set_addons_paths→save_database_repository, which canprompt and rewrite the data store. That is far more behaviour than
odev neutralizeshould carry.Tests
New
tests/tests/common/test_odoobin.py(expansion finds nested modules, ignores non-addonsdirectories, deduplicates, is computed once, and an explicit
[]stays[]), newtests/tests/common/test_local_database.py(script collection and order, modules without a scriptor not installed are skipped, and neutralize returns promptly when the process is unavailable), and
new
tests/tests/commands/test_quickstart.pyasserting the repository is set on the database handedto
restoreand still set once the command returns — the guard against someone simplifying thedouble link away. One fixture added,
addon_01/data/neutralize.sql. Full test suite passes.Linked Issues
Compliance
docsdirectoryrequirements.txtfile, if any🤖 Generated with Claude Code
https://claude.ai/code/session_01K8csZBrrBYp8oqH5paxTAm